{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Java 多态\n",
    "多态是同一个行为具有多个不同表现形式或形态的能力。\n",
    "\n",
    "## 多态的优点\n",
    "1. 消除类型之间的耦合关系\n",
    "2. 可替换性\n",
    "3. 可扩充性\n",
    "4. 接口性\n",
    "5. 灵活性\n",
    "6. 简化性\n",
    "\n",
    "## 多态存在的三个必要条件\n",
    "- 继承\n",
    "- 重写\n",
    "- 父类引用指向子类对象\n",
    "\n",
    "当使用多态方式调用方法时，首先检查父类中是否有该方法，如果没有，则编译错误；如果有，再去调用子类的同名方法。\n",
    "\n",
    "多态的好处：可以使程序有良好的扩展，并可以对所有类的对象进行通用处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:11+0000",
     "start_time": "2020-11-02T12:11:15.939Z"
    }
   },
   "outputs": [],
   "source": [
    "abstract class Animal {  \n",
    "    abstract void eat();  \n",
    "}  \n",
    "  \n",
    "class Cat extends Animal {  \n",
    "    public void eat() {  \n",
    "        System.out.println(\"吃鱼\");  \n",
    "    }  \n",
    "    public void work() {  \n",
    "        System.out.println(\"抓老鼠\");  \n",
    "    }  \n",
    "}  \n",
    "  \n",
    "class Dog extends Animal {  \n",
    "    public void eat() {  \n",
    "        System.out.println(\"吃骨头\");  \n",
    "    }  \n",
    "    public void work() {  \n",
    "        System.out.println(\"看家\");  \n",
    "    }  \n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:11+0000",
     "start_time": "2020-11-02T12:11:42.264Z"
    }
   },
   "outputs": [],
   "source": [
    "public class Test {\n",
    "    public static void main() {\n",
    "      show(new Cat());  // 以 Cat 对象调用 show 方法\n",
    "      show(new Dog());  // 以 Dog 对象调用 show 方法\n",
    "                \n",
    "      Animal a = new Cat();  // 向上转型  \n",
    "      a.eat();               // 调用的是 Cat 的 eat\n",
    "      Cat c = (Cat)a;        // 向下转型  \n",
    "      c.work();        // 调用的是 Cat 的 work\n",
    "  }  \n",
    "            \n",
    "    public static void show(Animal a)  {\n",
    "      a.eat();  \n",
    "        // 类型判断\n",
    "        if (a instanceof Cat)  {  // 猫做的事情 \n",
    "            Cat c = (Cat)a;  \n",
    "            c.work();  \n",
    "        } else if (a instanceof Dog) { // 狗做的事情 \n",
    "            Dog c = (Dog)a;  \n",
    "            c.work();  \n",
    "        }  \n",
    "    }  \n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:11+0000",
     "start_time": "2020-11-02T12:11:48.352Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "吃鱼\n",
      "抓老鼠\n",
      "吃骨头\n",
      "看家\n",
      "吃鱼\n",
      "抓老鼠\n"
     ]
    }
   ],
   "source": [
    "Test.main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 虚函数\n",
    "虚函数的存在是为了多态。\n",
    "\n",
    "Java 中其实没有虚函数的概念，它的普通函数就相当于 C++ 的虚函数，动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性，可以加上 final 关键字变成非虚函数。\n",
    "\n",
    "## 重写\n",
    "我们将介绍在 Java 中，当设计类时，被重写的方法的行为怎样影响多态性。\n",
    "\n",
    "我们已经讨论了方法的重写，也就是子类能够重写父类的方法。\n",
    "\n",
    "当子类对象调用重写的方法时，调用的是子类的方法，而不是父类中被重写的方法。\n",
    "\n",
    "要想调用父类中被重写的方法，则必须使用关键字 **super**。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:18+0000",
     "start_time": "2020-11-02T12:18:11.977Z"
    }
   },
   "outputs": [],
   "source": [
    "public class Employee {\n",
    "   private String name;\n",
    "   private String address;\n",
    "   private int number;\n",
    "   public Employee(String name, String address, int number) {\n",
    "      System.out.println(\"Employee 构造函数\");\n",
    "      this.name = name;\n",
    "      this.address = address;\n",
    "      this.number = number;\n",
    "    }\n",
    "    \n",
    "    public void mailCheck() {\n",
    "      System.out.println(\"邮寄支票给： \" + this.name + \" \" + this.address);\n",
    "   }\n",
    "   \n",
    "   public String toString() {\n",
    "      return name + \" \" + address + \" \" + number;\n",
    "   }\n",
    "   \n",
    "   public String getName() {\n",
    "      return name;\n",
    "   }\n",
    "   \n",
    "   public String getAddress() {\n",
    "      return address;\n",
    "   }\n",
    "   \n",
    "   public void setAddress(String newAddress) {\n",
    "      address = newAddress;\n",
    "   }\n",
    "   \n",
    "   public int getNumber() {\n",
    "     return number;\n",
    "   }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:19+0000",
     "start_time": "2020-11-02T12:19:15.833Z"
    }
   },
   "outputs": [],
   "source": [
    "public class Salary extends Employee{\n",
    "   private double salary; // 全年工资\n",
    "   public Salary(String name, String address, int number, double salary) {\n",
    "       super(name, address, number);\n",
    "       setSalary(salary);\n",
    "   }\n",
    "   \n",
    "   public void mailCheck() {\n",
    "       System.out.println(\"Salary 类的 mailCheck 方法 \");\n",
    "       System.out.println(\"邮寄支票给：\" + getName() + \" ，工资为：\" + salary);\n",
    "   }\n",
    "   \n",
    "   public double getSalary() {\n",
    "       return salary;\n",
    "   }\n",
    "   \n",
    "   public void setSalary(double newSalary) {\n",
    "       if(newSalary >= 0.0) {\n",
    "          salary = newSalary;\n",
    "       }\n",
    "   }\n",
    "   \n",
    "   public double computePay() {\n",
    "      System.out.println(\"计算工资，付给：\" + getName());\n",
    "      return salary/52;\n",
    "   }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:19+0000",
     "start_time": "2020-11-02T12:19:51.945Z"
    }
   },
   "outputs": [],
   "source": [
    "public class VirtualDemo {\n",
    "   public static void main() {\n",
    "      Salary s = new Salary(\"员工 A\", \"北京\", 3, 3600.00);\n",
    "      Employee e = new Salary(\"员工 B\", \"上海\", 2, 2400.00);\n",
    "      System.out.println(\"使用 Salary 的引用调用 mailCheck -- \");\n",
    "      s.mailCheck();\n",
    "      System.out.println(\"\\n使用 Employee 的引用调用 mailCheck--\");\n",
    "      e.mailCheck();\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-11-02T12:20+0000",
     "start_time": "2020-11-02T12:20:00.026Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Employee 构造函数\n",
      "Employee 构造函数\n",
      "使用 Salary 的引用调用 mailCheck -- \n",
      "Salary 类的 mailCheck 方法 \n",
      "邮寄支票给：员工 A ，工资为：3600.0\n",
      "\n",
      "使用 Employee 的引用调用 mailCheck--\n",
      "Salary 类的 mailCheck 方法 \n",
      "邮寄支票给：员工 B ，工资为：2400.0\n"
     ]
    }
   ],
   "source": [
    "VirtualDemo.main();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 例子解析\n",
    "- 实例中，实例化了两个 Salary 对象：一个使用 Salary 引用 s，另一个使用 Employee 引用 e。\n",
    "- 当调用 s.mailCheck() 时，编译器在编译时会在 Salary 类中找到 mailCheck()，执行过程 JVM 就调用 Salary 类的 mailCheck()。\n",
    "- e 是 Employee 的引用，但引用 e 最终运行的是 Salary 类的 mailCheck() 方法。\n",
    "- 在编译的时候，编译器使用 Employee 类中的 mailCheck() 方法验证该语句， 但是在运行的时候，Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。\n",
    "\n",
    "以上整个过程被称为虚拟方法调用，该方法被称为虚拟方法。\n",
    "\n",
    "Java中所有的方法都能以这种方式表现，因此，重写的方法能在运行时调用，不管编译的时候源代码中引用变量是什么数据类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多态的实现方式\n",
    "## 方式一：重写：\n",
    "Java 重写(Override)与重载(Overload)。\n",
    "## 方式二：接口\n",
    "## 方式三：抽象类和抽象方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java",
   "language": "java",
   "name": "java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "11.0.8+10-post-Ubuntu-0ubuntu120.04"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
